package com.leyou.search.service;

import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.vo.PageResult;
import com.leyou.item.clients.ItemClient;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.pojo.Goods;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchService {

    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private ItemClient itemClient;

    public PageResult<Goods> pageQuery(SearchRequest searchRequest) {

        //封装查询条件
        QueryBuilder query = buildBasicQuery(searchRequest);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //添加查询条件
        queryBuilder.withQuery(query);

        //添加分页条件,springData中分页从0开始，现实中分页从1开始，所以要-1
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage() - 1, searchRequest.getSize()));

        //执行查询
        AggregatedPage<Goods> goodsAggregatedPage = this.esTemplate.queryForPage(queryBuilder.build(), Goods.class);

        //封装返回分页结果，三个参数，总个数，总页数，当前页的元素内容
        return new PageResult<>(goodsAggregatedPage.getTotalElements(), goodsAggregatedPage.getTotalPages(), goodsAggregatedPage.getContent());
    }



    public Map<String, List<?>> filterQuery(SearchRequest searchRequest) {

        //封装查询条件
        QueryBuilder query = buildBasicQuery(searchRequest);

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //添加查询条件
        queryBuilder.withQuery(query);

        //可有可无，存在就是优化，减少输出，springData中分页每一页至少展示1个
        queryBuilder.withPageable(PageRequest.of(0, 1));

        String brandAggName = "brands";
        String categoryAggName = "categories";

        //添加品牌以及分类聚合条件
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("categoryId"));


        AggregatedPage<Goods> goodsAggregatedPage = this.esTemplate.queryForPage(queryBuilder.build(), Goods.class);

        //获取所有的聚合结果
        Aggregations aggregations = goodsAggregatedPage.getAggregations();

        //根据聚合名称解析聚合
        LongTerms brandTerms = aggregations.get(brandAggName);

        //从聚合分桶中获取聚合的品牌id集合
        List<Long> brandIds = brandTerms.getBuckets().stream()
                .map(LongTerms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        LongTerms categoryTerms = aggregations.get(categoryAggName);

        //从聚合分桶中，获取分类的id集合
        List<Long> categoryIds = categoryTerms.getBuckets().stream()
                .map(LongTerms.Bucket::getKeyAsNumber)
                .map(Number::longValue)
                .collect(Collectors.toList());

        //TODO 要添加健壮性判断，如果品牌id或者分类id为空则不应再去做查询

        Map<String, List<?>> result = new LinkedHashMap<>();

        result.put("分类", this.itemClient.queryCategoryByIds(categoryIds));
        result.put("品牌", this.itemClient.queryBrandByIds(brandIds));

        //展示其他搜索过滤条件，展示的前提是，分类确定并且唯一

        if (null != categoryIds || 1 == categoryIds.size()) {
            //参数1，表示分类，参数2，表示查询条件，参数3，表示结果封装
            getSpecs(categoryIds.get(0), query, result);
        }

        return result;
    }

    private void getSpecs(Long cid, QueryBuilder query, Map<String, List<?>> result) {
        //基于分类查询所有的可搜索规格参数
        List<SpecParamDTO> specParamDTOS = this.itemClient.querySpecParams(null, cid, true);


        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        queryBuilder.withQuery(query);

        queryBuilder.withPageable(PageRequest.of(0, 1));

        //添加聚合条件
        specParamDTOS.forEach(specParamDTO -> {
            String name = specParamDTO.getName();
            //聚合名称就是可搜索规格参数的名称，用来聚合的字段名称前面拼接specs.后面拼接.keyword
            queryBuilder.addAggregation(AggregationBuilders.terms(name).field("specs." + name + ".keyword"));
        });

        //执行查询
        AggregatedPage<Goods> goodsAggregatedPage = this.esTemplate.queryForPage(queryBuilder.build(), Goods.class);
        //解析聚合


        //获取所有的聚合结果
        Aggregations aggregations = goodsAggregatedPage.getAggregations();

        specParamDTOS.forEach(specParamDTO -> {
            //规格参数名称
            String name = specParamDTO.getName();

            //根据聚合名称获取聚合
            StringTerms stringTerms = aggregations.get(name);

            //根据聚合名称从聚合结果中取出对应分桶信息，取出分桶中的key，转为list集合
            List<String> values = stringTerms.getBuckets()
                    .stream().map(StringTerms.Bucket::getKeyAsString)
                    .collect(Collectors.toList());

            result.put(name, values);
        });

    }


    //查询构建方法
    private QueryBuilder buildBasicQuery(SearchRequest searchRequest) {
        //判断key是否有值
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)) {
            throw new LyException(ExceptionEnum.INVALID_REQUEST_PARAM);
        }

        //创建boolean查询(过滤要发生在boolean查询中)
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

        //给boolean查询添加一个实际的查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", searchRequest.getKey()).operator(Operator.AND));



        //获取到了所有的点击的过滤条件
        Map<String, String> filter = searchRequest.getFilter();

        //循环把过滤条件添加到filter过滤对象中
        filter.entrySet().forEach(entry->{
            //选中的规格key
            String key1 = entry.getKey();
            if ("品牌".equals(key1)){
                key1 = "brandId";
            }else if ("分类".equals(key1)){
                key1 = "categoryId";
            }else{
                //为什么由于查询走的是termQuery，如果不加keyword，字段可分词
                key1 = "specs."+key1+".keyword";
            }

            //给boolean查询添加过滤条件
            queryBuilder.filter(QueryBuilders.termQuery(key1,entry.getValue()));
        });



        return queryBuilder;
    }
}
